
#include "kernel.h"
#include "string.h"
#include <boost/date_time/posix_time/posix_time.hpp>
#include "aux-math.h"

using namespace WtfEngine;
using namespace boost::posix_time;

#define TIME_FIXED_DELTA	(1.0 / 60.0)

/**** Events ****/

void EventHandler::setHandler(const Functor::tRef& rHandler) {
	mrHandler = rHandler;
	mBindTime = Kernel::GetInstance().GetTime();
};

EventRefCmp EventChannel::gEventRefCmp;

Event::tEventStatus EventChannel::ProcessEvent(const Event::tRef& ev, volatile Event::tEventStatus& Status) {
	if(rEvent->Accepts(*ev)) {
		tEventMap::iterator Channel;

		//
		// We process more specialised event handlers first. These
		// are found by lookup in the channel's child event map.
		//

		Channel = LookupChannel(ev);
		if(Channel != vEventMap.end()) {
			Status = Channel->second->ProcessEvent(ev, Status);
		};

		for(int i = 0; i < vEventHandlerQueue.GetSize(); i++) {
			// Stop processing on stop status
			if(Status == Event::STOPPED)
				break;

			vEventHandlerQueue.Get(i).Invoke();
		};
	};

	return Status;
};

EventChannel::tEventMap::iterator EventChannel::LookupChannel(const Event::tRef& ev) {
	return vEventMap.find(ev);
};

EventChannel::tEventMap::const_iterator EventChannel::LookupChannel(const Event::tRef& ev) const {
	return vEventMap.find(ev);
};

void EventChannel::BindEvent(const Event::tRef& ev, const EventHandler& eh) {
	EventChannel::tEventMap::iterator	Channel = LookupChannel(ev);

	//
	// We need to maintain the invariant on the primary tree, so
	// if the specified event accepts the current channel's event
	// it needs to
	//

	if(Channel == vEventMap.end()) {
		// No channel exists - create a new one
		
		EventChannel * pNewChannel = new EventChannel(ev);
		InsertChannel(pNewChannel);
		pNewChannel->InsertHandler(eh);

	} else if(Channel->first->Accepts(*ev)) {
		if(ev->Accepts(*Channel->first)) {
			
			//
			// Events are semantically equal, so simply add the new handler
			// to the existing channel.
			//

			Channel->second->InsertHandler(eh);
		} else {
			// Subset
			Channel->second->BindEvent(ev, eh);
		};
	} else {
		if(ev->Accepts(*Channel->first)) {
			
			//
			// Existing channel is a subset of the new channel. Need to
			// swap them to maintain the invariant.
			//

			EventChannel::tRef rTmpChannel = Channel->second;
			const_cast<Event::tRef&>(Channel->first) = ev; // XXX- HACK: should remove and re-insert
			Channel->second = new EventChannel(ev);
			Channel->second->InsertChannel(rTmpChannel);
			Channel->second->InsertHandler(eh);

			// Other channels may also be subsets of this new channel, so scan the map and move them.
			for(EventChannel::tEventMap::iterator i = vEventMap.begin(); i != vEventMap.end();) {
				if(ev->Accepts(*(i->first))) {
					Channel->second->InsertChannel(i->second);
#ifdef _MSC_VER
					i = vEventMap.erase(i);
#else
					vEventMap.erase(i++);
#endif
				};
			};
		} else {
			throw new std::logic_error("BindEvent: event map corruption!");
		};
	};
};

void EventChannel::UnbindEvent(const Event::tRef& ev, const EventHandler& eh)
{
	if(ev->Accepts(*rEvent)) {
		RemoveHandler(eh);
	} else {
		tEventMap::iterator i = LookupChannel(ev);
		if( i != vEventMap.end() ) {
			i->second->UnbindEvent(ev, eh);
			if(i->second->CountHandlers() <= 0) {
				vEventMap.erase(i);
			};
		};
	};
};

void EventChannel::ClearEvents(const Event::tRef& ev) {
	if(ev->Accepts(*rEvent)) {
		for(tEventMap::const_iterator Channel = vEventMap.begin(); Channel != vEventMap.end(); Channel++) {
			Channel->second->ClearEvents(ev);
		};

		vEventHandlerQueue.Clear();
		vEventHandlers.clear();
		vEventMap.clear();
	} else {
		tEventMap::iterator Channel = LookupChannel(ev);	
		if(Channel != vEventMap.end()) {
			Channel->second->ClearEvents(ev);
			if(Channel->second->CountHandlers() <= 0) {
				vEventMap.erase(Channel);
			};
		};
	};
};

void EventChannel::RemoveHandler(const EventHandler& eh) {
	for(tEventMap::const_iterator Channel = vEventMap.begin(); Channel != vEventMap.end();) {
		Channel->second->RemoveHandler(eh);
		if(Channel->second->CountHandlers() <= 0) {
#		ifdef MSC_VER
			Channel = vEventMap.erase(Channel);
#		else
			vEventMap.erase(Channel++);
#		endif
		} else {
			Channel++;
		};
	};

	// Eww.. this is heavily dependant on the priority queue implementation
	// being a heap.
	for(int i = 0; i < vEventHandlerQueue.GetSize();) {
		if(eh == *vEventHandlerQueue.Get(i).pHandler) {
			vEventHandlerQueue.Remove(i);
		} else {
			i++;
		};
	};
	vEventHandlers.remove(eh);
};

unsigned EventChannel::CountHandlers() const {
	unsigned uCount = 0;
	for(tEventMap::const_iterator Channel = vEventMap.begin(); Channel != vEventMap.end(); Channel++) {
		uCount += Channel->second->CountHandlers();
	};
	return uCount + vEventHandlers.size();
};

EventChannel::tRef EventChannel::Clone() const {
	tRef rNewChannel = new EventChannel(rEvent);

	// Copy everything (deep copy)
	for(tEventMap::const_iterator Channel = vEventMap.begin(); Channel != vEventMap.end(); Channel++) {
		rNewChannel->InsertChannel(Channel->second->Clone());
	};
	for(int i = 0; i < vEventHandlerQueue.GetSize(); i++) {
		rNewChannel->InsertHandler(*vEventHandlerQueue.Get(i).pHandler);
	};

	return rNewChannel;
};

/**** Game state ****/

GameState::GameState()
: mbUpdateParent(false), EventChannel(new Event())
{
	mGameStart = Kernel::GetInstance().GetTime();
	mGameTime = 0;
	mTimeScale = 1.0;
	mrEventChannel = new EventChannel(new Event());

#ifdef TIME_FIXED_DELTA
	mTimeDelta = TIME_FIXED_DELTA;
#endif
};

GameState::~GameState(){

};

void GameState::Update(tGameTime dt)
{
	mTimeDelta = dt * mTimeScale;
	mGameTime += mTimeDelta;
	if(mbUpdateParent && !mParent.isNull()) mParent->Update(dt);
};


/**
 * Copies the event map from this game state into the new
 * state.
 **/
GameState::tRef	GameState::Fork(GameState* pNewState)
{
	//
	// Copy the event map to the new event
	//

	pNewState->mrEventChannel = mrEventChannel->Clone();

	pNewState->mbUpdateParent = true;
	pNewState->mGameTime = mGameTime;
	pNewState->mTimeDelta = mTimeDelta;
	return GameState::tRef(pNewState);
};

void GameState::Disable() {

};

void GameState::Destroy() {
	// Clear all event handlers
	this->ClearEvents(new Event());
};


/**** Task Management / Synchro ****/

void TaskCore::Pause() {
	this->Status = PAUSED;
};
void TaskCore::Resume() {
	this->Status = READY;
};
void TaskCore::Wait() {
	this->Status = WAITING;
};
void TaskCore::Wake() {
	this->Status = READY;
};

void TaskCore::SetProfiler(bool bStart)
{
	static tGameTime rt;
	if(bStart){
		rt = Kernel::GetInstancePtr()->GetTime();
		mNumInvocations++;
	}else{
		mTotalRunTime += Kernel::GetInstancePtr()->GetTime() - rt;
	};
};

void TaskCore::DumpProfilerData() const
{
	std::cout << Name << ": RunTime = " << mTotalRunTime << ", "
		<< mNumInvocations << " invocations, Avg = " << mTotalRunTime / mNumInvocations
		<< std::endl;
};



/**** The Kernel ****/

void Kernel::PushGameState(GameState* pNewState) {
	pNewState->mParent = State;
	State = pNewState;
	if(!State->mParent.isNull())
		State->mParent->Disable();
	State->Initialize();
	State->Enable();
};
GameState::tRef Kernel::ForkGameState(GameState* pNewState) {
	GameState::tRef rNewState(State->Fork(pNewState));
	PushGameState(pNewState);
	return rNewState;
};
void Kernel::PopGameState(GameState::tRef* prState){
	*prState = State;
	State = State->mParent;
	(*prState)->Disable();
	(*prState)->Destroy();
	if(!State.isNull())
		State->Enable();
};
void Kernel::PopGameState()
{
	GameState::tRef rState = State;
	State = State->mParent;
	rState->Disable();
	rState->Destroy();
	if(!State.isNull())
		State->Enable();
};

Event::tEventStatus Kernel::ProcessEvent(const WtfEngine::Event::tRef &ev) {
	Event::tEventStatus tmpStatus;

	// Set event pointer
	mvCurrentEventStatus.push_back(Event::PROPAGATING);
	mvpCurrentEvent.push_back(&*ev);
	int i = mvCurrentEventStatus.size() - 1;

	// Kernel event handlers take priority over game state event handlers,
	// so process them first.
	tmpStatus = mrEventChannel->ProcessEvent(ev, mvCurrentEventStatus[i]);
	if(tmpStatus != Event::STOPPED) {
		tmpStatus = State->ProcessEvent(ev, mvCurrentEventStatus[i]);
	};

	// Clear pointer.
	mvpCurrentEvent.pop_back();
	mvCurrentEventStatus.pop_back();
	return tmpStatus;
};

void Kernel::BindEvent(const Event::tRef& ev, const EventHandler& eh) {
	mrEventChannel->BindEvent(ev, eh);
};
void Kernel::UnbindEvent(const Event::tRef& ev, const EventHandler& eh) {
	mrEventChannel->UnbindEvent(ev, eh);
};
void Kernel::ClearEvents(const Event::tRef& ev) {
	mrEventChannel->ClearEvents(ev);
};
void Kernel::RemoveHandler(const EventHandler& eh) {
	mrEventChannel->RemoveHandler(eh);
};

Event::tEventStatus Kernel::SetEventStatus(Event::tEventStatus st) {
	int i = mvCurrentEventStatus.size() - 1;
	assert(i > 0);
	mvCurrentEventStatus[i] = (st > mvCurrentEventStatus[i] ? st : mvCurrentEventStatus[i]);
	return mvCurrentEventStatus[i];
};


tGameTime Kernel::GetTime() const {
	return this->mRunTime;
};

void	Kernel::RunTask(TaskCore* 	task)
{
	// Queue the task
	this->InsertTask(task);
};

void	Kernel::WaitTask(TaskCore*  task)
{
	if((task->Status == task->RUNNING)
		|| (task->Status == task->READY)) {

		// Call Wait routine
		task->Wait();
	} else throw "Cannot wait - task is not running";
};

void	Kernel::WakeTask(TaskCore* task)
{
	if(task->Status == task->WAITING)
		this->InsertTask(task);

	// Call OnWake
	task->Wake();
};

void	Kernel::PauseTask(TaskCore* task)
{
	if((task->Status == task->RUNNING)
		|| (task->Status == task->READY)) {

		// Call OnWait routine
		task->Pause();
	} else throw "Cannot pause - task is not running";
};

void	Kernel::ResumeTask(TaskCore* task)
{
	if(task->Status != task->PAUSED)
		throw "Task is not paused - cannot resume";

	// Call OnResume
	task->Resume();

	// Run it
	this->InsertTask(task);
};

void	Kernel::InsertTask(TaskCore* task)
{
	// Set to running
	task->Status = task->RUNNING;

	// Insert into priority queue
	this->RunQueue.Insert(task, task->Priority);
};

Kernel::Kernel()
: mrEventChannel(new EventChannel(new Event()))
{
	this->SystemStatus = SYSTEM_INITIALIZING;
	initRandom();

	mvCurrentEventStatus.push_back(Event::NONE);
};

Kernel::~Kernel()
{
	this->FlushLog();
};

void Kernel::FlushLog()
{
	
};

void Kernel::DumpProfilerData()
{
	int i;
	for(i = 0; i < RunQueue.GetSize(); i++)
		RunQueue.Get(i).Value->DumpProfilerData();
};


